2023.08.08 FuncAnimationを使いやすく【matplotlib】
数値シミュレーションの結果をGIFアニメ化するときに、Pillowを利用すると全フレームの画像ファイルを生成する必要があり効率が悪い。
そういう場合には FuncAnimation を利用するとよいのだが、これ自身いくつかの関数や処理用の変数を要求するため、複数のGIFアニメを生成させるには関数名や変数名の管理が面倒である。そこで、
1つのGIFアニメあたり1つのオブジェクトで生成
プロットに必要な関数や変数はオブジェクト内で完結させる
ことを目的として、以下のサンプルコードを用意した。実行環境はWSLである。
ファイル名:anime01.py
code:python
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
class Anime_GIF:
def __init__(self, filename, color, maxframes):
self.fig, self.ax = plt.subplots()
self.color = color
self.maxframes = maxframes
self.ani = FuncAnimation(
fig = self.fig,
func = self.plot_func,
frames = range(self.maxframes), # イテレータでフレーム数を設定
repeat = True,
cache_frame_data = False,
interval = 100 # フレーム間の時間をmsec単位で指定
)
self.x = np.linspace(0, 10, 100)
# aniオブジェクトのframeオプションで与えたイテレータが生成する値が
# plotのframeに渡されてプロットが行われる
self.ani.save(filename) # GIFアニメに保存
# plt.show() # コメントアウトすると画面に表示
# プロットの内容を記述する関数
# aniオブジェクトのframesに与えたイテレータの値が順に引数frameに渡される
def plot_func(self, frame):
# プロットに必要な数値の準備
y = np.sin(self.x*frame/10)
# 直前の内容をクリア
self.ax.cla()
# 現フレームのグラフを描画
plt.title('DoDeSuKa')
self.ax.grid()
self.ax.set_xlim(0, 10)
self.ax.set_ylim(-1.2, 1.2)
self.ax.text(8.0, 1.0, str(frame) + '/' + str(self.maxframes), size=20)
self.ax.plot(self.x, y, color=self.color)
filename1 = 'anime1_1.gif'
ani1 = Anime_GIF(filename1, 'red', 10)
# filename2 = 'anime1_2.gif'
# ani2 = Anime_GIF(filename2, 'blue', 30)
要点としては
プロットに必要なデータはインスタンス変数として保持させる
複数グラフをプロットする場合に、最上位のオブジェクト名(クラス)だけを管理すればよいので、プログラムの見通しがよい。
複数の異なるグラフを生成する例を考える。
ファイル名:anime02.py
code:python
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
import pandas as pd
class Anime_GIF1:
def __init__(self):
self.fig, self.ax = plt.subplots()
self.color = 'red'
self.maxframes = 50
filename = 'anime2_1.gif'
self.ani = FuncAnimation(
fig = self.fig,
func = self.plot_func,
frames = range(self.maxframes), # イテレータでフレーム数を設定
repeat = True,
cache_frame_data = False,
interval = 100 # フレーム間の時間をmsec単位で指定
)
self.ani.save(filename) # GIFアニメとして保存
plt.show() # こちらを有効化すると画面に表示する
def plot_func(self, frame):
x = np.linspace(0, 10, 100)
y = np.sin(x*frame/10)
self.ax.cla()
# 現フレームのグラフを描画
plt.title('DoDeSuKa')
self.ax.grid()
self.ax.set_xlim(0, 10)
self.ax.set_ylim(-1.2, 1.2)
self.ax.text(8.0, 1.0, str(frame) + '/' + str(self.maxframes), size=20)
self.ax.plot(x, y, color=self.color)
class Anime_GIF2:
def __init__(self):
self.fig, self.ax = plt.subplots()
df = pd.read_csv('data.csv')
self.maxframes = len(df)
data = df.to_numpy()
self.x1, self.y1 = data:,1, data:,2
self.x2, self.y2 = data:,4, data:,5
self.ani = FuncAnimation(
fig = self.fig,
func = self.plot_func,
frames = range(self.maxframes), # イテレータでフレーム数を設定
repeat = True,
cache_frame_data = False,
interval = 100 # フレーム間の時間をmsec単位で指定
)
self.ani.save('anime2_2.gif') # GIFアニメとして保存
plt.show() # こちらを有効化すると画面に表示する
def plot_func(self, frame):
p1 = (0, self.x1frame, self.x2frame)
p2 = (0, self.y1frame, self.y2frame)
self.ax.cla()
plt.title('DoDeSuKa')
self.ax.grid()
self.ax.set_xlim(-2.2, 2.2)
self.ax.set_ylim(-2.2, 2.2)
self.ax.text(1.5, 1.5, str(frame) + '/' + str(self.maxframes), size=20)
self.ax.plot(p1, p2, color='blue')
self.ax.plot(self.x2frame, self.y2frame, 'x', color='red')
ani1 = Anime_GIF1()
ani2 = Anime_GIF2()
GIFアニメの生成にあたって、シミュレーションは終了し、その結果はcsvファイルに保存されている状態を想定している。
上のサンプル(anime02.py)では次のデータファイルを利用してください。
ファイル名:data.csv
code:text
0,1,0,0,2,0
0.1,0.995004165,0.099833417,0.05,1.993754426,0.149812586
0.2,0.980066578,0.198669331,0.1,1.975070743,0.298502747
0.3,0.955336489,0.295520207,0.15,1.944107567,0.444958339
0.4,0.921060994,0.389418342,0.2,1.901127572,0.588087673
0.5,0.877582562,0.479425539,0.25,1.846494984,0.726829498
0.6,0.825335615,0.564642473,0.3,1.780672104,0.86016268
0.7,0.764842187,0.644217687,0.35,1.7042149,0.987115495
0.8,0.696706709,0.717356091,0.4,1.617767703,1.106774433
0.9,0.621609968,0.78332691,0.45,1.522057071,1.218292444
1,0.540302306,0.841470985,0.5,1.417884868,1.320896523
1.1,0.453596121,0.89120736,0.55,1.306120643,1.413894589
1.2,0.362357754,0.932039086,0.6,1.187693369,1.496681559
1.3,0.267498829,0.963558185,0.65,1.063582627,1.568744591
1.4,0.169967143,0.98544973,0.7,0.93480933,1.629667417
1.5,0.070737202,0.997494987,0.75,0.802426071,1.679133747
1.6,-0.029199522,0.999573603,0.8,0.667507187,1.716929694
1.7,-0.128844494,0.99166481,0.85,0.531138652,1.742945216
1.8,-0.227202095,0.973847631,0.9,0.394407874,1.757174541
1.9,-0.323289567,0.946300088,0.95,0.258393523,1.759715592
2,-0.416146837,0.909297427,1,0.124155469,1.750768412
2.1,-0.504846105,0.863209367,1.05,-0.007275057,1.730632592
2.2,-0.588501117,0.808496404,1.1,-0.134904996,1.699703764
2.3,-0.666276021,0.745705212,1.15,-0.25778858,1.658469152
2.4,-0.737393716,0.675463181,1.2,-0.375035961,1.607502267
2.5,-0.801143616,0.598472144,1.25,-0.485821253,1.547456763
2.6,-0.856888753,0.515501372,1.3,-0.589389925,1.479059557
2.7,-0.904072142,0.42737988,1.35,-0.685065455,1.403103238
2.8,-0.942222341,0.33498815,1.4,-0.772255198,1.32043788
2.9,-0.970958165,0.239249329,1.45,-0.850455396,1.23196232
3,-0.989992497,0.141120008,1.5,-0.919255295,1.138614995
3.1,-0.99913515,0.041580662,1.55,-0.978340322,1.041364427
3.2,-0.998294776,-0.058374143,1.6,-1.027494298,0.94119946
3.3,-0.98747977,-0.157745694,1.65,-1.066600659,0.839119334
3.4,-0.966798193,-0.255541102,1.7,-1.095642687,0.736123708
実行結果をいったんcsvファイルに保存するという方針に基づくことにより、プロットは計算が終了した時点で行えばよいことになる。従って、これらのプロット用プログラムはシミュレーションプログラム本体とは分割して作ることができるので、いろいろと楽になる。
データのファイルへの書き込みは2023.6.20 日付+時刻を名称とするフォルダの作成が参考になる。